/*
 * FolderDescription.cpp
 *
 *  Created on: 04.05.2011
 *      Author: niea
 */

#include "FolderDescription.hpp"
#include "../common/log.hpp"
#include "../common/Utils.hpp"
#include <exception>

using namespace rapidxml;
namespace efs
{

FileDescription::FileDescription()
	: file_type(FileType_file)
	, file_status(FileStatus_stored)
	, size(0)
{

}

void FileDescription::print() const
{
	std::string type;
	switch (file_type)
	{
	case FileType_file :
			type = "file";
			break;
	case FileType_folder :
			type = "folder";
	}

	std::string status;
	switch (file_status)
	{
	case FileStatus_stored:
			status = "stored";
			break;
	case FileStatus_modified:
			status = "modified";
			break;
	case FileStatus_deleted:
			status = "deleted";
			break;
	case FileStatus_conflict:
			status = "conflict";
			break;
		FileStatus_out_of_scope:
		status = "out_of_scope";
	}

	pantheios::log_DEBUG(" type=[" + type + "] status=[" + status + "] size=" + intToStr(size) + " path= " + path);
}

bool FileDescription::operator==(FileDescription const &fd) const
{
	if(
			file_type == fd.file_type &&
			file_status == fd.file_status &&
			path == fd.path
	)
	{
		return true;
	}
	else
	{
		return false;
	}
}

FolderDescription::FolderDescription()
	: m_first_start(true)
	, m_first_announce(true)
{
}

FolderDescription::FolderDescription(const std::string & root_path)
    : m_root_path(root_path)
	//, m_first_start(true)
{
	scanFolder();
	m_first_start = true;
	m_first_announce = true;
}

FolderDescription::~FolderDescription()
{

}

void FolderDescription::setRootPath(std::string const &path)
{
	m_root_path = path;
	scanFolder();
	m_first_start = true;
}

void FolderDescription::clear()
{
	m_files.clear();
}

const std::vector<FileDescription> & FolderDescription::getFolderDescription() const
{
	return m_files;
}

void FolderDescription::toXML(rapidxml::xml_document<> &xml, rapidxml::xml_node<> *root_node)
{
	xml_node<> *folder_root = xml.allocate_node(node_element, "folder");
	root_node->append_node(folder_root);
	folder_root->append_attribute(xml.allocate_attribute("path", xml.allocate_string(m_root_path.c_str(), m_root_path.length()+1 )));
	xml_node<> *entry = NULL;
	std::vector<FileDescription>::const_iterator it = m_files.begin();

	for (; it != m_files.end(); ++it)
	{
		std::string s_type = intToStr(it->file_type);
		std::string s_status = intToStr(it->file_status);
		std::string s_size = intToStr(it->size);

		entry = xml.allocate_node(node_element, "entry");
		entry->append_attribute(xml.allocate_attribute("path", xml.allocate_string(it->path.c_str(), it->path.length()+1 )));
		entry->append_attribute(xml.allocate_attribute("type", xml.allocate_string(s_type.c_str(), s_type.length()+1 )));
		entry->append_attribute(xml.allocate_attribute("status", xml.allocate_string(s_status.c_str(), s_status.length()+1 )));
		entry->append_attribute(xml.allocate_attribute("size", xml.allocate_string(s_size.c_str(), s_size.length()+1 )));

		folder_root->append_node(entry);
	}

	if (m_first_announce)
	{
		m_first_announce = false;
		root_node->append_attribute(xml.allocate_attribute("first-announce", ""));
	}

}

void FolderDescription::fromXML(rapidxml::xml_document<> &xml, rapidxml::xml_node<> *root_node)
{
	 m_files.clear();

	xml_node<> *folder_root = root_node->first_node("folder");
	xml_node<> *entry = folder_root->first_node("entry");
	while (entry)
	{
		FileDescription file_description;
		file_description.path = entry->first_attribute("path")->value();
		file_description.file_type = (FileType)strToInt(entry->first_attribute("type")->value());
		file_description.file_status = (FileStatus)strToInt(entry->first_attribute("status")->value());
		file_description.size = strToInt(entry->first_attribute("size")->value());
		m_files.push_back(file_description);

		entry = entry->next_sibling("entry");
	}
}

void FolderDescription::print(std::vector<FileDescription> const &files) const
{
	pantheios::log_DEBUG("[FolderDescription]: printing file list, size = " + intToStr(files.size()));
	std::vector<FileDescription>::const_iterator it = files.begin();
	for (; it != files.end(); ++it)
	{
		it->print();
	}
}

void FolderDescription::splitEntryList(std::vector<FileDescription> const& list,
		std::vector<std::string> &files,
		std::vector<std::string> &folders) const
{
	files.clear();
	folders.clear();

	std::vector<FileDescription>::const_iterator it = list.begin();
	for (; it != list.end(); ++it)
	{
		if (it->file_type == FileType_file)
		{
			files.push_back(it->path);
		}
		else
		{
			folders.push_back(it->path);
		}
	}
}

void FolderDescription::applyMyChanges(std::vector<FileDescription> const& changes_list)
{
	std::vector<FileDescription>::const_iterator it = changes_list.begin();
	for (; it != changes_list.end(); ++it)
	{
		std::vector<FileDescription>::iterator it_my = m_files.begin();
		for (; it_my != m_files.end(); ++it_my)
		{
			if (it->path == it_my->path && it_my->file_status == FileStatus_modified)
			{
				it_my->file_status == FileStatus_stored;
				it_my->size = fileSize(m_root_path + it->path);
			}
		}
	}
}

void FolderDescription::print() const
{
	print(m_files);
}

int FolderDescription::itemsCount() const
{
	return m_files.size();
}

bool FolderDescription::scanFolder()
{
	std::vector<FileDescription> files;
	try
	{
		scanFolder(files, m_root_path, "");
	}
	catch(std::exception const& ex)
	{
		return false;
	}
	//if (!m_files.empty())
	if (!m_first_start)
	{
		files = compareNewVersion(m_files, files);
		bool is_equal = isEqual(m_files, files);
		m_files = files;
		return !is_equal;
	}
	else
	{
		m_first_start = false;
		m_files = files;
		std::vector<FileDescription>::iterator it_new = m_files.begin();
		for (; it_new != m_files.end(); ++it_new)
		{
			if (it_new->file_status == FileStatus_stored)
			{
				it_new->file_status = FileStatus_modified;
			}
		}
		return true;
	}
}

void FolderDescription::scanFolder(std::vector<FileDescription> & files, const std::string & root_path, const std::string & path) const
{
	//files.clear();
	DIR * directory;
	struct dirent * directory_ent;
	directory = opendir((root_path + path).c_str());

	if (directory == NULL)
	{
		pantheios::log_ERROR("[FolderDescription]: Open directory fail, path=" + path);
		return;
	}

	//pantheios::log_DEBUG("folder parsing start, path" + path);
	while ((directory_ent = readdir(directory)))
	{
		std::string entry_name = directory_ent->d_name;
		std::string entry_path = path + entry_name;
		DIR * is_dir = opendir((root_path + entry_path).c_str());
		closedir(is_dir);

		//pantheios::log_DEBUG("entry_name : " + entry_name);
		//pantheios::log_DEBUG("entry_path : " + root_path + entry_path);
		if ((entry_name.compare(".") == 0) || (entry_name.compare("..") == 0))
		{
			continue;
		}

		FileDescription file_description;
		file_description.path = entry_path;
		file_description.file_status = FileStatus_stored;

		if (is_dir != NULL)
		{
			//pantheios::log_DEBUG("is_folder : " + entry_name);
			file_description.file_type = FileType_folder;
		}
		else
		{
			//entry->append_attribute(xml.allocate_attribute("type",  "file"));
			//entry->append_node(addFileMeta(xml, root_path + entry_path));
			file_description.file_type = FileType_file;
			file_description.size = fileSize(root_path + entry_path);
			if (file_description.size == -1)
			{
				pantheios::log_ERROR("[FolderDescription]: File size -1 [" + root_path + entry_path + "]");
				closedir(directory);
				throw std::exception();
			}
		}
		files.push_back(file_description);
		if (file_description.file_type == FileType_folder)
		{
			scanFolder(files, root_path, path + entry_name + "/");
		}
	}
	closedir(directory);
}


std::vector<FileDescription> FolderDescription::compareNewVersion(std::vector<FileDescription> files_old,
		std::vector<FileDescription> files_new) const
{
	std::vector<FileDescription> result = files_new;
	std::vector<FileDescription>::iterator it_old = files_old.begin();
	std::vector<FileDescription>::iterator it_new = result.begin();

	for (; it_old != files_old.end(); ++it_old)
	{
		it_new = result.begin();
		for (; it_new != result.end(); ++it_new)
		{
			if (it_old->path == it_new->path)
			{

				if (it_old->file_status == FileStatus_deleted)
				{
					it_new->file_status = FileStatus_modified;
				}
				else
				{
					it_new->file_status = it_old->file_status;
				}
				if (it_new->file_status == FileStatus_stored && it_new->size != it_old->size)
				{
					it_new->file_status = FileStatus_modified;
				}

				break;
			}
		}
		if (it_new == result.end())
		{
			it_old->file_status = FileStatus_deleted;
			result.push_back(*it_old);

		}
	}
	return result;
}

void FolderDescription::compareExternal(std::vector<FileDescription> const &c_files_ext,
		std::vector<FileDescription> &get_list,
		std::vector<FileDescription> &remove_list,
		bool first_announce)
{
	get_list.clear();
	remove_list.clear();
	std::vector<FileDescription> files_ext = c_files_ext;
	std::vector<FileDescription>::iterator it_ext = files_ext.begin();
	for (; it_ext != files_ext.end(); ++it_ext)
	{
		if (it_ext->file_status == FileStatus_conflict)
		{
			//it_ext->file_status == FileStatus_stored;
		}
	}

	it_ext = files_ext.begin();
	for (; it_ext != files_ext.end(); ++it_ext)
	{
		//FileDescription file = getFileByPass(file_ext, it_int->path);
		std::vector<FileDescription>::iterator it_int = getFileByPath(m_files, it_ext->path);

		/* we have not this file */
		if (it_int == m_files.end())
		{
			if (it_ext->file_status != FileStatus_deleted && it_ext->file_status != FileStatus_conflict)
			{
				get_list.push_back(*it_ext);
			}
			continue;
		}

		/* ignoring */
		if (it_int->file_status == FileStatus_conflict || it_ext->file_status == FileStatus_conflict )
		{
			it_int->file_status = FileStatus_conflict;
			continue;
		}


		/* if we deleted this file in the past*/
		if (it_int->file_status == FileStatus_deleted)
		{
			if (it_ext->file_status != FileStatus_deleted)
			{
				get_list.push_back(*it_ext);
			}
			continue;
		}

		/* if we already have this file */
		if (it_int->file_status == FileStatus_stored)
		{
			if (it_int->size != it_ext->size && (it_ext->file_status == FileStatus_stored || first_announce))
			{
				pantheios::log_DEBUG("[FolderDescription]: adding conflict 1");
				it_int->print();
				it_int->file_status = FileStatus_conflict;
				continue;
			}
			if (it_int->size != it_ext->size && it_ext->file_status == FileStatus_modified)
			{
				//it_int->size = it_ext->size;
				get_list.push_back(*it_ext);
				continue;
			}
			if (it_ext->file_status == FileStatus_deleted)
			{
				remove_list.push_back(*it_ext);
				continue;
			}
			continue;
		}

		/* if we already have this file modified */
		if (it_int->file_status == FileStatus_modified)
		{
			if (it_int->size == it_ext->size && (it_ext->file_status == FileStatus_stored || it_ext->file_status == FileStatus_modified))
			{
				it_int->file_status = FileStatus_stored;
				continue;
			}
			else
			{
				pantheios::log_DEBUG("[FolderDescription]: adding conflict 2");
				it_int->file_status = FileStatus_conflict;
				it_int->print();
				continue;
			}
		}
	}
}

std::vector<FileDescription>::iterator FolderDescription::getFileByPath(std::vector<FileDescription> &files_ext,
																		std::string const &path)
{
	std::vector<FileDescription>::iterator it_ext = files_ext.begin();
	for (; it_ext != files_ext.end(); ++it_ext)
	{
		if (it_ext->path == path && it_ext->file_status != FileStatus_conflict)
		{
			return it_ext;
		}
	}
	return files_ext.end();
}


bool FolderDescription::isEqual(std::vector<FileDescription> files_1,
		std::vector<FileDescription> files_2)
{
	std::vector<FileDescription>::iterator it = files_1.begin();
	for (; it != files_1.end(); ++it)
	{
		std::vector<FileDescription>::iterator it2 = files_2.begin();
		for (; it2 != files_2.end(); ++it2)
		{
			if (*it == *it2)
			{
				files_2.erase(it2);
				break;
			}
		}
	}
	return files_2.empty();
}

}

